import "winston-daily-rotate-file";

import { utilities as nestWinstonModuleUtilities } from "nest-winston";
import { ClsService } from "nestjs-cls";
import * as winston from "winston";
import { format } from "winston";

import * as PACKAGE_JSON from "../../../package.json";
import { REQUEST_ID_KEY } from "../constant/global-enum";

const { combine, timestamp, json, splat } = winston.format;

// ========================
// 工具函数：过滤敏感字段
// ========================
const removeSensitiveData = info => {
    const sensitiveFields = ["password", "token", "accessToken", "refreshToken", "secret"];
    const redacted = "[REDACTED]";

    const sanitize = obj => {
        if (typeof obj !== "object" || obj === null) return obj;

        const cleaned = Array.isArray(obj) ? [] : {};
        for (const [key, value] of Object.entries(obj)) {
            if (sensitiveFields.some(field => key.toLowerCase().includes(field))) {
                cleaned[key] = redacted;
            } else if (typeof value === "object" && value !== null) {
                cleaned[key] = sanitize(value);
            } else {
                cleaned[key] = value;
            }
        }
        return cleaned;
    };

    // 深拷贝并清理 meta 数据
    const cleanedMeta = sanitize({ ...info });
    delete cleanedMeta.message;
    delete cleanedMeta.level;
    delete cleanedMeta.timestamp;

    return {
        ...info,
        ...cleanedMeta
    };
};

// ========================
// 格式化器
// ========================

// JSON 格式（用于文件输出）
const fileFormat = combine(
    splat(), // 支持 logger.info('msg %s', var)
    timestamp({ format: "YYYY-MM-DD HH:mm:ss.SSS" }),
    winston.format(info => removeSensitiveData(info))(),
    json()
);

// 错误日志格式（用于错误日志文件，包含堆栈信息）
export const createErrorFileFormat = (clsService: ClsService) => {
    return format.combine(
        format.timestamp({ format: "YYYY-MM-DD HH:mm:ss.SSS" }),
        format.errors({ stack: true }), // 确保错误堆栈被正确序列化
        format(info => {
            // 自定义转换函数，添加 reqId 等字段保存到文件
            const reqId = clsService.get(REQUEST_ID_KEY) || "N/A";
            const { stack, ...meta } = info.stack[0] || {};
            return {
                name: meta?.errorName,
                timestamp: info.timestamp,
                level: info.level.toUpperCase(),
                reqId: reqId,
                message: info.message,
                context: info.context,
                stack: stack || null,
                meta: meta
            };
        })(),
        format.json()
    );
};

// ========================
// 传输器
// ========================

/**
 * 错误日志：仅 error 及以上，单独文件
 */
// 创建错误日志传输
export const createErrorTransport = (clsService: ClsService) => {
    const errorTransport = new winston.transports.DailyRotateFile({
        level: "error",
        dirname: "logs",
        filename: "error-%DATE%.log",
        format: createErrorFileFormat(clsService),
        datePattern: "YYYY-MM-DD",
        zippedArchive: true,
        maxSize: "20m",
        maxFiles: "30d" // 保留30天
    });
    return errorTransport;
};

/**
 * 全量日志：包含了 info 及以上的所有日志，如启动日志、错误日志等，单独文件
 */
export const combinedTransport = new winston.transports.DailyRotateFile({
    level: "info",
    filename: "logs/combined-%DATE%.log",
    datePattern: "YYYY-MM-DD",
    format: fileFormat,
    zippedArchive: true,
    maxSize: "50m",
    maxFiles: "14d"
});

/**
 * 控制台日志：winston 默认仅 info 及以上，才会控制台输出。这里格式采用 nest 内置日志服务的格式
 */
export const consoleTransport = new winston.transports.Console({
    level: "debug",
    format: winston.format.combine(
        winston.format.timestamp(), // 添加时间戳
        // winston.format.ms(), // 添加耗时（可选）
        nestWinstonModuleUtilities.format.nestLike(PACKAGE_JSON.name, {
            // 类 nest 内置日志格式
            colors: true,
            prettyPrint: true,
            processId: true,
            appName: true
        })
    )
});

/**
 * 异常日志：uncaughtException 和 unhandledRejection
 * Winston 可自动捕获未处理的异常（uncaughtException）和 Promise rejection（unhandledRejection），并记录日志。
 */
export const exceptionTransport = new winston.transports.DailyRotateFile({
    filename: "logs/exceptions.log",
    datePattern: "YYYY-MM-DD",
    format: fileFormat,
    zippedArchive: true,
    maxSize: "20m",
    maxFiles: "30d"
});

/**
 * rejection 日志：unhandledRejection
 * Winston 可自动捕获未处理的异常（uncaughtException）和 Promise rejection（unhandledRejection），并记录日志。
 */
export const rejectionTransport = new winston.transports.DailyRotateFile({
    filename: "logs/rejections.log",
    datePattern: "YYYY-MM-DD",
    format: fileFormat,
    zippedArchive: true,
    maxSize: "20m",
    maxFiles: "30d"
});
